package com.zhang.hadoop.netty.nio.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author zhang yufei
 * @date 2022/1/10 5:14 下午
 * @description: 服务器端与客户端之间的通讯
 **/
public class NIOServer {

    public static void main(String[] args) {
        try {
            //创建 ServerSocketChannel -> ServerSocket
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //得到一个 selector 对象
            Selector selector = Selector.open();
            //绑定端口,在服务器端监听
            serverSocketChannel.socket().bind(new InetSocketAddress(8000));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //把serverSocketChannel 注册到 selector,关心事件为 OP_ACCEPT(连接)
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //循环等待客户端连接
            while (true) {
                //这离我们等待 1 秒,如果没有事件繁盛,返回
                if (selector.select(1000) == 0) {
                    System.out.println("服务器等待 1 秒,无连接");
                    continue;
                }
                //如果返回>0,解惑到相关的 selectionKey 集合
                //1.如果返回的>0,表示已经获取到关注的事件
                //2.selector.selectedKeys()返回关注时间的集合
                //3.通过selectionKeys反向获取通道
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();
                //遍历selectionKeySet,使用迭代器
                Iterator<SelectionKey> selectionKeyIterator = selectionKeySet.iterator();
                while (selectionKeyIterator.hasNext()) {
                    //获取到selectionKey
                    SelectionKey selectionKey = selectionKeyIterator.next();
                    //根据 key 对应的通道发生定时间做相应处理
                    if (selectionKey.isAcceptable()) {//如果是 OP_ACCEPT,有新的客户端连接
                        //给该客户端生成一个 SocketChannel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("客户端连接成功,生成一个 socketChannel "+socketChannel.hashCode());
                        //将 socketChannel 设置为非阻塞
                        socketChannel.configureBlocking(false);
                        //将 socketChannel 注册到 selector,关注事件为 OP_READ
                        //同时个 socketChannel 关联一个 buffer
                        socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    }
                    if (selectionKey.isReadable()) {//发生 OP_READ
                        //通过 key,反向获取到对应channel
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                        channel.read(buffer);
                        System.out.println("form 客户端 " + new String(buffer.array()));
                    }
                    //手动从集合中一处当前的 selectionKey,防止重复操作
                    selectionKeyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
